package gang.org.springframework.framework.factory;

import gang.org.springframework.framework.bean.GangBeanDefinition;
import gang.org.springframework.framework.bean.GangBeanWrapper;
import gang.org.springframework.framework.bean.GangRootBeanDefinition;
import gang.org.springframework.framework.beanpostprocessor.GangBeanPostProcessor;
import gang.org.springframework.framework.beanpostprocessor.GangInstantiationAwareBeanPostProcessor;
import gang.org.springframework.framework.beanpostprocessor.GangMergedBeanDefinitionPostProcessor;
import gang.org.springframework.framework.beanpostprocessor.GangSmartInstantiationAwareBeanPostProcessor;
import gang.org.springframework.framework.support.GangResolvableType;
import gang.org.springframework.framework.support.GangNullBean;
import gang.org.springframework.framework.util.GangBeanFactoryUtils;
import gang.org.springframework.framework.util.GangClassUtils;
import gang.org.springframework.framework.util.GangObjectUtils;
import gang.org.springframework.framework.util.GangStringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;


/**
 * @author gang.chen
 * @description
 * @Date 2021/6/16 19:08
 */
public abstract class GangAbstractBeanFactory extends GangFactoryBeanRegistrySupport implements GangConfigurableBeanFactory{


    public final Map<String,GangRootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);


    public final List<GangBeanPostProcessor> beanPostProcessors = new GangBeanPostProcessorCacheAwareList();

    public ClassLoader beanClassLoader = GangClassUtils.getDefaultClassLoader();

    public final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

    public boolean cacheBeanMetadata = true;

    public volatile GangBeanPostProcessorCache beanPostProcessorCache;


    @Override
    public <T> T getBean(String name, Class<T> requiredType) {
        return doGetBean(name,requiredType,null,false);
    }

    protected <T> T doGetBean(String name,Class<T> requiredType, Object[] args, boolean typeCheckOnly){

        //TODO
        String beanName = transformedBeanName(name);

        //TODO
        Object beanInstance = null;

        /*
         * 业务意义
         * beanName = mvcResourceUrlProvider 第一次获取为空
         * */
        Object sharedInstance = getSingleton(beanName);
        /*
         * 业务意义
         * 1）bean = mvcResourceUrlProvider 对应的工厂类 "GangWebMvcAutoConfiguration$GangEnableWebMvcConfiguration"
         * 2）初始化 mvcResourceUrlProvider前，要获取工厂FactoryBean = GangWebMvcAutoConfiguration$GangEnableWebMvcConfiguration
         * */
        if (sharedInstance != null && args == null) {
            //TODO
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }else {

            //条件一
            if (isPrototypeCurrentlyInCreation(beanName)) {
                //TODO
            }

            //条件二
            GangBeanFactory parentBeanFactory = null;
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                //TODO
            }

            //条件三
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            //StartupStep up
            try{
                /*
                 * 1） beanName = mvcResourceUrlProvider, 首先由Springboot启动时加载为BeanDefinition
                 * */
                GangRootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

                checkMergedBeanDefinition(mbd, beanName, args);

                //条件1
                String[] dependOn = null;
                if (dependOn != null) {
                    //TODO
                }

                //条件2
                if (mbd.isSingleton()) {

                    sharedInstance = getSingleton(beanName, () -> {
                        return createBean(beanName,mbd,args);
                    });
                    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

                //条件3

                //条件4

            }
            catch (Exception e){
                // TODO
            }
            finally {
                // TODO
            }
        }



        return adaptBeanInstance(name, beanInstance, null);
    }

    protected String transformedBeanName(String name){
        return canonicalName(GangBeanFactoryUtils.transformedBeanName(name));
    };


    /**
     * 业务意义，首次从bean name，到创建BeanDefinition
     * SpringbootManualApplication.main(String[] args)
     * GangSpringApplication.run(Class, String)
     * GangSpringApplication.run(String[])
     * GangSpringApplication.refresh(GangConfigurableApplicationContext)
     * GangAbstractApplicationContext.registerBeanPostProcessor(GangConfigurableListableBeanFactory)
     * GangPostProcessorRegistrationDelegate.registerBeanPostProcessors(GangConfigurableListableBeanFactory,GangAbstractApplicationContext)
     * GangDefaultListableBeanFactory.getBeanNamesForType(Class, boolean, boolean)
     * GangDefaultListableBeanFactory.doGetBeanNamesForType(GangResolvableType, boolean, boolean)
     * GangAbstractBeanFactory.getMergedLocalBeanDefinition(String)
     * 问题一：为什么要使用mergedBeanDefinitions
     * 回答：1，mergedBeanDefinitions集合中的元素BeanDefinition是通过bean名称获取所得
     * 问题二：beanDefinition=mvcResourceUrlProvider是什么时机，保存到beanDefinitionMap？
     * 回答：mvcResourceUrlProvider是通过方法invokeBeanFactoryPostProcessor在完成spring.factories解析后，存储到集合中
     * */
    protected GangRootBeanDefinition getMergedLocalBeanDefinition(String beanName){
        /*
         * 业务意义
         * 1）mbd -> gang.org.springframework.framework.web.servlet.GangWebMvcAutoConfiguration$GangEnableWebMvcConfiguration
         *    mbd.resolvedTargetType 缺失，value = GangWebMvcAutoConfiguration$GangEnableWebMvcConfiguration
         *    mbd.beanClass 缺失， value = GangWebMvcAutoConfiguration$GangEnableWebMvcConfiguration
         * */
        GangRootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);

        if (mbd != null && !mbd.stale) {
            return mbd;
        }
        /*
         * 业务意义
         * 1，spring.factories配置文件，注解的bean被存储于GangDefaultListableBeanFactory->Map
         * 2，DeanDefinition对象类型-> ConfigurationClassBeanDefinition
         * 3，ConfigurationClassBeanDefinition
         *      constructorArgumentValues = null
         * 4，beanDefinition = mvcResourceUrlProvider，通过方法invokeBeanFactoryPostProcessor，解析spring.factories得到
         * */
        return getMergedBeanDefinition(beanName,getBeanDefinition(beanName));
    }

    protected GangRootBeanDefinition getMergedBeanDefinition(String beanName, GangBeanDefinition bd){
        return getMergedBeanDefinition(beanName,bd,null);
    }

    /**
     * @param bd is instance to class of {@link GangRootBeanDefinition}
     * 业务意义
     * 1）bd这是由方法invokeBeanFactoryPostProcessor解析spring.factories而来
     * */
    protected GangRootBeanDefinition getMergedBeanDefinition(
            String beanName,
            GangBeanDefinition bd,
            GangBeanDefinition containingBd){

        /**
         * 业务意义
         * 1，GangBeanDefinition 接口实现类一 GangRootBeanDefinition -> Springboot对核心类例如，E.g GangConfigurationClassPostProcessor
         * 2，GangBeanDefinition 接口实现类二 GangAnnotatedGenericBeanDefinition -> Springboot启动类，E.g SpringbootManualApplication
         * */
        synchronized (this.mergedBeanDefinitions) {
            GangRootBeanDefinition mbd = null;
            GangRootBeanDefinition previous = null;

            /*
             * 场景一T
             * 业务意义
             * 1）mergedBeanDefinitions集合中，什么时间点，mvcResourceUrlProvider存入
             * 2）判定是否有合并的beanDefinition
             * */
            if (containingBd == null) {
                mbd = this.mergedBeanDefinitions.get(beanName);
            }
            /*
             * 场景二
             * 1）如果BeanDefinition未创建
             * 2）如果BeanDefinition创建中（注：stale = true表示当前的BeanDefinition在创建中）
             * */
            if (mbd == null || mbd.stale) {
                previous = mbd;
                //2.1
                if (bd.getParentName() == null) {

                    if (bd instanceof GangRootBeanDefinition) {
                        mbd = ((GangRootBeanDefinition) bd).cloneBeanDefinition();
                    }else {
                        mbd = new GangRootBeanDefinition(bd);
                    }
                }else {
                    //TODO BeanDefinition子结点：完成合并
                }
                //2.2
                if (!GangStringUtils.hasLength(mbd.getScope())) {
                    mbd.setScope(SCOPE_SINGLETON);
                }
                //2.3 TODO

                //2.4 TODO
                if (containingBd == null && isCacheBeanMetadata()) {
                    this.mergedBeanDefinitions.put(beanName,mbd);
                }

            }

            // 场景三
            if (previous != null) {
                copyRelevantMergedBeanDefinitionCaches(previous, mbd);
            }

            return mbd;
        }


    }

    protected abstract GangBeanDefinition getBeanDefinition(String beanName);

    protected abstract Object createBean(String beanName, GangRootBeanDefinition mbd, Object[] args);

    protected Class<?> predictBeanType(String beanName, GangRootBeanDefinition mbd, Class<?>... typesToMath){
        return null;
    }

    /**
     * 业务意义
     * 判断beanName是否为一个FactoryBean，E.g “mvcResourceUrlProvider”
     * @param beanName BeanDefinition名称
     * @param mbd  BeanDefinition 实例
     * 1）resolvedTargetType 赋值
     * 2）isFactoryBean 赋值
     * 3）factoryMethodReturnType 赋值
     * 4）factoryMethodToIntrospect 赋值
     * 5）predictBeanType()方法首次被调用
     * */
    protected boolean isFactoryBean(String beanName, GangRootBeanDefinition mbd){
        Boolean result = mbd.isFactoryBean;

        /**
         * 业务意义
         * 1）输入参数一：BeanDefinition名称
         * 2）输入参数二：BeanDefinition对象
         * 3）返回该BeanDefinition对应的Class类
         * */
        if (result == null) {
            Class<?> beanType = predictBeanType(beanName, mbd, null);
            result = (beanType != null && GangFactoryBean.class.isAssignableFrom(beanType));
            mbd.isFactoryBean = result;
        }
        return result;
    }

    /**
     * 业务意义
     * 1）以给定的接口class类为参数
     * 2）从BeanDefinition的集合里，获取实例类
     * */
    protected Class<?> resolveBeanClass(GangRootBeanDefinition mbd, String beanName, Class<?>... typesToMatch){
        /**
         * 场景一
         * Springboot 内部BeanDefinition
         * 场景二
         * Springboot spring.factories工厂 BeanDefinition
         * 场景三
         * Springboot 工厂类方法注解@Bean BeanDefinition
         * */
        try{
            /**
             * 场景一
             * Springboot 内部BeanDefinition
             * */
            if (mbd.hasBeanClass()) {
                return mbd.getBeanClass();
            }
            /**
             * 场景二
             * Springboot spring.factories工厂 BeanDefinition
             * */
            if (1 == 2) {
                //TODO
                return null;
            }
            /**
             * 场景三
             * Springboot 内部BeanDefinition
             * */
            else {
                return doResolveBeanClass(mbd, typesToMatch);
            }
        }
        catch (Exception e)
        {
            throw e;
        }
    }

    /**
     * TODO
     * 业务意义
     * 1，传入一个接口对象（注：该接口对象被封装为GangResolvableTye）
     * 2，传入一人字符对象，最终：判定字符对应的对象是否是接口的一个实现了
     * 3，name为BeanDefinition的名称，typeToMatch指定接口类名，判定该BeanDefinition是否是typeToMatch的实现类
     * @param name Springboot解析的BeanDefinition名字
     * @param typeToMatch 接口类
     *
     * E.g name-> mvcResourceUrlProvider;typeToMatch -> GangApplicationListener
     * 业务意义：mvcResourceUrlProvider bean指向->类GangResourceUrlProvider->该类继承自类->gang.org.springframework.framework.context.GangApplicationListener
     *
     * return true/false
     * */
    protected boolean isTypeMatch(String name, GangResolvableType typeToMatch, boolean allowFactoryBeanInit){

        String beanName = transformedBeanName(name);
        boolean isFactoryDereference = GangBeanFactoryUtils.isFactoryDereference(name);

        // Check manually registered singletons. 处理 requestMappingHandlerMapping
        Object beanInstance = getSingleton(beanName,false);
        if (beanInstance != null && beanInstance.getClass() != GangNullBean.class)
        {
            if (beanInstance instanceof GangFactoryBean) {

                if (!isFactoryDereference){
                    //TODO
                }else{
                    //TODO
                }

            }else if(!isFactoryDereference)
            {
                // 处理 requestMappingHandlerMapping
                if (typeToMatch.isInstance(beanInstance)) {

                    return true;
                }
                //else if  TODO

                    //if() TODO

                        //if TODO

                        //if TODO

                    //if()

                    //return


            }
            return false;
        }
        else if(containsSingleton(beanName) && !containsBeanDefinition(beanName)){

            //TODO

        }

        // No singleton instance found -> check bean definition TODO


        // Retrieve corresponding bean definition. TODO
        GangRootBeanDefinition mdb = getMergedLocalBeanDefinition(name);
        //BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); TODO

        // Set up the types that we want to match against. TODO
        Class<?> classToMatch = typeToMatch.resolve();
        if(classToMatch == null){
            //TODO
        }

        Class<?>[] typesToMatch = new Class<?>[]{classToMatch}; //TODO


        // Attempt to predict the bean type TODO
        Class<?> predictedType = null;


        //if (!isFactoryDereference && dbd != null && isFactoryBean(beanName, mbd)) TODO


        // If we couldn't use the target type, try regular prediction.
        if(predictedType == null){
            predictedType = predictBeanType(name,mdb,typesToMatch);
            if (predictedType == null) {
                return false;
            }
        }

        // Attempt to get the actual ResolvableType for the bean.
        // ResolvableType beanType = null; TODO
        //if (FactoryBean.class.isAssignableFrom(predictedType)) {
        //else if()


        //if (beanType == null) { TODO


        //if (beanType != null) { TODO


        // If we don't have a bean type, fallback to the predicted type
        return typeToMatch.isAssignableFrom(predictedType);
    }

    protected Object getObjectForBeanInstance(Object beanInstance,String name, String beanName, GangRootBeanDefinition mbd){
        //TODO
        return beanInstance;
    }

    <T> T adaptBeanInstance(String name, Object bean, Class<?> requiredType){
        return (T)bean;
    }

    //重写CopyOnWriteArrayList
    private class GangBeanPostProcessorCacheAwareList extends CopyOnWriteArrayList<GangBeanPostProcessor>{

        @Override
        public GangBeanPostProcessor set(int index, GangBeanPostProcessor element) {
            GangBeanPostProcessor result = super.set(index, element);
            beanPostProcessorCache = null;
            return result;
        }

        @Override
        public boolean add(GangBeanPostProcessor gangBeanPostProcessor) {
            boolean result = super.add(gangBeanPostProcessor);
            beanPostProcessorCache = null;
            return true;
        }

        @Override
        public void add(int index, GangBeanPostProcessor element) {
            super.add(index, element);
            beanPostProcessorCache = null;
        }
    }

    public void addBeanPostProcessors(Collection<? extends GangBeanPostProcessor> beanPostProcessors){
        this.beanPostProcessors.removeAll(beanPostProcessors);
        this.beanPostProcessors.addAll(beanPostProcessors);
    }

    private Class<?> doResolveBeanClass(GangRootBeanDefinition mbd, Class<?>... typeToMatch){
        ClassLoader beanClassLoader = getBeanClassLoader();

        //TODO 逻辑判断一

        //TODO 逻辑判断二

        //TODO 逻辑判断三

        return mbd.resolveBeanClass(beanClassLoader);
    }

    @Override
    public ClassLoader getBeanClassLoader() {
        return this.beanClassLoader;
    }

    @Override
    public Class<?> getType(String name) {
        return getType(name, true);
    }

    @Override
    public Class<?> getType(String name, boolean allowFactoryBeanInit) {
        String beanName = transformedBeanName(name);

        //TODO check manually registered singletons

        //TODO No singleton instance found -> check bean definition

        /*
         * 1）gang.org.springframework.framework.web.servlet.GangWebMvcAutoConfiguration$GangEnableWebMvcConfiguration
         * 2）org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
         *
         * 问题1，mbd = GangWebMvcAutoConfiguration$GangEnableWebMvcConfiguration 什么阶段初始化
         * */
        GangRootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

        //TODO Check decorated bean definition, if any: We assume it'll be easier

        Class<?> beanClass = predictBeanType(beanName, mbd);

        //TODO Check bean class whether we're dealing with a FactoryBean.
        if(1 == 2){
            //TODO
            return null;
        }else {
            //TODO return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
            return beanClass;
        }
    }

    /**
     * 业务意义：验证bean factory 是否包含指定的 bean definition 通过输入参数
     * @param beanName bean 名称
     * @return boolean value
     * */
    public abstract boolean containsBeanDefinition(String beanName);

    /**
     * 业务意义：判定BeanDefinition是否为一个FactoryBean
     * */
    @Override
    public boolean isFactoryBean(String name) {
        return false;
    }

    @Override
    public Object getBean(String name) {
        return doGetBean(name, null, null, false);
    }



    /**
     * 判断当前bean是否为一个原型bean
     * @param beanName bean的名称
     * @return boolean value 该方法需要进一步扩张 TODO
     * */
    protected boolean isPrototypeCurrentlyInCreation(String beanName){
        return false;
    }

    /**
     * 业务意义
     * 给指定的输入bean标识一个状态，明确该bean已经在创建中
     * @param beanName bean的名称
     * */
    protected void markBeanAsCreated(String beanName){
        if (!this.alreadyCreated.contains(beanName)) {
            synchronized (this.mergedBeanDefinitions) {
                if (!this.alreadyCreated.contains(beanName)) {

                    clearMergedBeanDefinition(beanName);

                    this.alreadyCreated.add(beanName);
                }

            }
        }
    }

    protected void clearMergedBeanDefinition(String beanName){
        GangRootBeanDefinition bd = this.mergedBeanDefinitions.get(beanName);
        if (bd != null) {
            bd.stale = true;
        }
    }

    @Override
    public boolean isCacheBeanMetadata() {
        return cacheBeanMetadata;
    }

    /**
     * 业务意义
     * 1）问题：beanDefinition = mvcResourceUrlProvider，在什么时间被初始化
     *    回答：1）该对象在GangWebMvcAutoConfiguration中被注解
     * @param mbd BeanDefinition 实例
     * @param beanName Bean的名称
     * @param args 启动参数
     * */
    protected void checkMergedBeanDefinition(GangRootBeanDefinition mbd, String beanName, Object[] args){
        // TODO
        if (mbd.isAbstract()) {

        }
    }

    /**
     * 业务意义
     * 1）previous -> mvcResourceUrlProvider
     * 2）mbd      -> mvcResourceUrlProvider
     * 3）两个BeanDefinition的属性值进行复制
     * */
    private void copyRelevantMergedBeanDefinitionCaches(GangRootBeanDefinition previous, GangRootBeanDefinition mbd){
        String mbdBeanClassName = mbd.getBeanClassName();
        String previousBeanClassName = previous.getBeanClassName();

        String mbdFactoryBeanName = mbd.getFactoryBeanName();
        String previousFactoryBeanName = previous.getFactoryBeanName();

        String mbdFactoryMethodName = mbd.getFactoryMethodName();
        String previousFactoryMethodName = previous.getFactoryMethodName();

        boolean b0 = GangObjectUtils.nullSafeEquals(mbdBeanClassName, previousBeanClassName);
        boolean b1 = GangObjectUtils.nullSafeEquals(mbdFactoryBeanName, previousFactoryBeanName);
        boolean b2 = GangObjectUtils.nullSafeEquals(mbdFactoryMethodName, previousFactoryMethodName);
        if (b0 && b1 && b2) {
            GangResolvableType mbdTargetType = mbd.targetType;
            GangResolvableType previousTargetType = previous.targetType;

            if (mbdTargetType == null || mbdTargetType.equals(previousTargetType)) {
                mbd.targetType = previous.targetType;
                mbd.isFactoryBean = previous.isFactoryBean;
                mbd.resolvedTargetType = previous.resolvedTargetType;
                mbd.factoryMethodReturnType = previous.factoryMethodReturnType;
                mbd.factoryMethodToIntrospect = previous.factoryMethodToIntrospect;
            }
        }

    }

    //TODO
    public void registerDisposableBeanIfNecessary(String beanName, Object bean, GangRootBeanDefinition mbd){

    }

    public static class GangBeanPostProcessorCache{
        final List<GangInstantiationAwareBeanPostProcessor> instantiationAware = new ArrayList<>();
        final List<GangSmartInstantiationAwareBeanPostProcessor> smartInstantiationAware = new ArrayList<>();
        final List<GangMergedBeanDefinitionPostProcessor> mergedDefinition = new ArrayList<>();
    }

    public GangBeanPostProcessorCache getBeanPostProcessorCache(){
        GangBeanPostProcessorCache beanPostProcessorCache = this.beanPostProcessorCache;
        if (beanPostProcessorCache == null) {
            beanPostProcessorCache = new GangBeanPostProcessorCache();
            //从缓存中所有的BeanPostProcessor
            for (GangBeanPostProcessor bp : this.beanPostProcessors) {
                if (bp instanceof GangInstantiationAwareBeanPostProcessor) {
                    beanPostProcessorCache.instantiationAware.add((GangInstantiationAwareBeanPostProcessor) bp);

                    if (bp instanceof GangSmartInstantiationAwareBeanPostProcessor) {
                        beanPostProcessorCache.smartInstantiationAware.add((GangSmartInstantiationAwareBeanPostProcessor) bp);
                    }
                }
                //TODO 业务场景二

                //TODO 业务场景三
                if (bp instanceof GangMergedBeanDefinitionPostProcessor) {
                    beanPostProcessorCache.mergedDefinition.add((GangMergedBeanDefinitionPostProcessor) bp);
                }
            }
        }

        return beanPostProcessorCache;
    }

    public boolean hasInstantiationAwareBeanPostProcessors(){
        return !getBeanPostProcessorCache().instantiationAware.isEmpty();
    }

    public void initBeanWrapper(GangBeanWrapper bw){
        //TODO
    }

    @Override
    public void addBeanPostProcessor(GangBeanPostProcessor beanPostProcessor) {
        this.beanPostProcessors.remove(beanPostProcessor);
        this.beanPostProcessors.add(beanPostProcessor);
    }

    @Override
    public boolean isTypeMatch(String name, Class<?> typeToMatch) {
        return false;
    }

    @Override
    public boolean isTypeMatch(String name, GangResolvableType typeToMatch) {
        return false;
    }

    public List<GangBeanPostProcessor> getBeanPostProcessors() {
        return beanPostProcessors;
    }
}
